﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace MemoryScannerTestConsoleApp {
    public class ProcessInterop {


        [Flags]
        public enum MemoryProtection : uint {
            NO_CALLER_ACCESS = 0x0,
            PAGE_NOACCESS = 0x01,
            PAGE_READONLY = 0x02,
            PAGE_READWRITE = 0x04,
            PAGE_WRITECOPY = 0x08,
            PAGE_EXECUTE = 0x10,
            PAGE_EXECUTE_READ = 0x20,
            PAGE_EXECUTE_READWRITE = 0x40,
            PAGE_EXECUTE_WRITECOPY = 0x80,
            PAGE_GUARD = 0x100,
            PAGE_NOCACHE = 0x200,
            PAGE_WRITECOMBINE = 0x400
        }

        public enum MemoryState : uint {
            COMMIT  = 0x1000,
            FREE    = 0x10000,
            RESERVE = 0x2000
        }

        
        public enum MemoryType : uint {
            NO_CALLER_ACCESS = 0x0,
            IMAGE            = 0x1000000,
            MAPPED           = 0x40000,
            PRIVATE          = 0x20000
        }

        [Flags]
        public enum ProcessAccessFlags : uint {
            All              = 0x001F0FFF,
            /// <summary>Enables usage of the process handle in the TerminateProcess function to terminate the process.</summary>
            Terminate        = 0x00000001,
            /// <summary>Enables usage of the process handle in the CreateRemoteThread function to create a thread in the process.</summary>
            CreateThread     = 0x00000002,
            /// <summary>Enables usage of the process handle in the VirtualProtectEx and WriteProcessMemory functions to modify the virtual memory of the process.</summary>
            VMOperation      = 0x00000008,
            /// <summary>Enables usage of the process handle in the ReadProcessMemory function to' read from the virtual memory of the process.</summary>
            VMRead           = 0x00000010,
            /// <summary>Enables usage of the process handle in the WriteProcessMemory function to write to the virtual memory of the process.</summary>
            VMWrite          = 0x00000020,
            /// <summary>Enables usage of the process handle as either the source or target process in the DuplicateHandle function to duplicate a handle.</summary>
            DupHandle        = 0x00000040,
            /// <summary>Enables usage of the process handle in the SetPriorityClass function to set the priority class of the process.</summary>
            SetInformation   = 0x00000200,
            /// <summary>Enables usage of the process handle in the GetExitCodeProcess and GetPriorityClass functions to read information from the process object.</summary>
            QueryInformation = 0x00000400,
            /// <summary>Enables usage of the process handle in any of the wait functions to wait for the process to terminate.</summary>
            Synchronize      = 0x00100000
        }

        /// <summary>
        /// Indicates the manner in which data on the memory stack is aligned to different boundaries.
        /// The simplest definition is a rule that for byte alignment size n (n = 2^x, where x is some non negative integer),
        /// the set of alignment boundaries exist at n*b(b is all non negative integers).
        /// Any data with a size ds less than or equal to the byte alignment size n can only be placed
        /// on a location in memory m where n*b less than or equal to m which is less than or equal to n*b+(n-ds).
        /// Or put in words, data cannot be placed in locations that exist on two sides of a given alignment boundary
        /// unless it's size is greater than the alignment size.
        /// </summary>
        public enum DataAlignment { Default = 0, Byte1 = 1, Byte2 = 2, Byte4 = 4, Byte8 = 8 };



        /// <summary>
        /// Nothing to instantiate
        /// </summary>
        private ProcessInterop() { } 



        
#region unmanaged code
        /// <summary>
        /// Gets the process handle for further read/write operations
        /// </summary>
        /// <param name="dwDesiredAccess">DWORD access flag</param>
        /// <param name="bInheritHandle">BOOL inherit handle?</param>
        /// <param name="dwProcessId">DWORD process id</param>
        /// <returns>The process handle</returns>
        [DllImport( "kernel32.dll" )]
        static extern IntPtr OpenProcess( 
            ProcessAccessFlags dwDesiredAccess, 
            [MarshalAs( UnmanagedType.Bool )] bool bInheritHandle, 
            int dwProcessId );


        /// <summary>
        /// 
        /// </summary>
        /// <param name="hProcess">HANDLE process handle</param>
        /// <param name="lpBaseAddress">LPVOID </param>
        /// <param name="lpBuffer">LPVOID </param>
        /// <param name="nSize">DWORD </param>
        /// <param name="lpNumberOfBytesWritten">LPDWORD </param>
        /// <returns></returns>
        [DllImport( "kernel32.dll", SetLastError = true )]
        static extern bool WriteProcessMemory( 
            IntPtr hProcess, 
            IntPtr lpBaseAddress, 
            byte[] lpBuffer, //[in, out] to avoid extra copy operations?
            uint nSize, 
            out int lpNumberOfBytesWritten );

        /// <summary>
        /// 
        /// </summary>
        /// <param name="hProcess"></param>
        /// <param name="lpBaseAddress"></param>
        /// <param name="lpBuffer"></param>
        /// <param name="dwSize"></param>
        /// <param name="lpNumberOfBytesRead"></param>
        /// <returns></returns>
        [DllImport( "kernel32.dll", SetLastError = true )]
        static extern bool ReadProcessMemory(
          IntPtr hProcess,
          IntPtr lpBaseAddress,
          [Out] byte[] lpBuffer,
          int dwSize,
          out int lpNumberOfBytesRead
         );

        /// <summary>
        /// 
        /// </summary>
        /// <param name="hProcess"></param>
        /// <returns></returns>
        [DllImport( "kernel32.dll", SetLastError = true )]
        static extern bool CloseHandle( IntPtr hProcess );


        /// <summary>
        /// 
        /// </summary>
        /// <param name="hProcess"></param>
        /// <param name="lpAddress">A pointer to the base address of the region of of pages to be queried. 
        /// Rounded down to closest page boundary. If the value specifies an address higher than the process can access, the function fails. </param>
        /// <param name="lpBuffer">The struct that contains the memory info for the process</param>
        /// <param name="dwLength">Size of lpBuffer in bytes</param>
        /// <returns>success of query</returns>
        [DllImport( "kernel32.dll", SetLastError = true )]
        static extern bool VirtualQueryEx( IntPtr hProcess, IntPtr lpAddress, out MEMORY_BASIC_INFORMATION lpBuffer, uint dwLength );



#endregion





#region exposed wrapper process functions
        /// <summary>
        /// Retrieves a handle from an existing process
        /// </summary>
        /// <param name="desiredAccess">The requested permission level</param>
        /// <param name="inheritHandle">Do processes created from this one inherit the handle</param>
        /// <param name="processId">The Id of the process object</param>
        /// <returns>A handle for a running process</returns>
        /// <exception cref="System.ComponentModel.Win32Exception">Thrown when the returned processHandle is null</exception>
        public static IntPtr openProcessHandle( ProcessAccessFlags desiredAccess, bool inheritHandle, int processId ) {
            IntPtr processHandle = OpenProcess( desiredAccess, inheritHandle, processId );
            if ( processHandle == null ) {
                Int32 err = Marshal.GetLastWin32Error();
                throw new System.ComponentModel.Win32Exception( err );
            }
            return processHandle;
        }

        /// <summary>
        /// Closes an active handle for a process
        /// </summary>
        /// <param name="processHandle"></param>
        /// <exception cref="System.ComponentModel.Win32Exception">Thrown when the processHandle could not be closed</exception>
        public static void closeProcessHandle( IntPtr processHandle ) {
            if ( !CloseHandle( processHandle) ) {
                Int32 err = Marshal.GetLastWin32Error();
                throw new System.ComponentModel.Win32Exception( err );
            }
        }


        /// <exception cref="System.ComponentModel.Win32Exception">Thrown when the processHandle could not be read</exception>
        public static void readProcessMemory( IntPtr processHandle, IntPtr baseAddress, ref byte[] buffer, out int numberOfBytesRead ) {
            if ( !ReadProcessMemory( processHandle, baseAddress, buffer, buffer.Length, out numberOfBytesRead ) ) {
                Int32 err = Marshal.GetLastWin32Error();
                throw new System.ComponentModel.Win32Exception( err );
            }
        }


        public static void writeProcessMemory( IntPtr processHandle, IntPtr baseAddress, ref byte[] buffer, out int numberOfBytesRead ) {
            if ( !WriteProcessMemory( processHandle, baseAddress, buffer, (uint)buffer.Length, out numberOfBytesRead ) ) {
                Int32 err = Marshal.GetLastWin32Error();
                throw new System.ComponentModel.Win32Exception( err );
            }
        }


        public static void virtualQueryEx( IntPtr processHandle, IntPtr baseAddress, out MEMORY_BASIC_INFORMATION mbi, uint mbiSize ) {
            if ( !VirtualQueryEx( processHandle, baseAddress, out mbi, mbiSize ) ) {
                Int32 err = Marshal.GetLastWin32Error();
                throw new System.ComponentModel.Win32Exception( err );
            }
        }



#endregion




    }

    /// <summary>
    /// Contains information about a range of pages in the virtual address space of a process. 
    /// </summary>
    [StructLayout( LayoutKind.Sequential )]
    public struct MEMORY_BASIC_INFORMATION {
        /// <summary>
        /// A pointer to the base address(resolution in bytes) of the region of pages.
        /// </summary>
        public IntPtr BaseAddress;
        /// <summary>
        /// A pointer to the base address of a range of pages allocated by the VirtualAlloc function. 
        /// The page pointed to by the BaseAddress member is contained within this allocation range.
        /// </summary>
        public IntPtr AllocationBase;
        /// <summary>
        /// The memory protection option when the region was initially allocated. 
        /// This member can be one of the memory protection constants or 0 if the caller does not have access.
        /// </summary>
        public ProcessInterop.MemoryProtection AllocationProtect;
        /// <summary>
        /// The size of the region beginning at the base address in which all pages have identical attributes, in bytes.
        /// </summary>
        public IntPtr RegionSize;
        /// <summary>
        /// The state of the pages in the region. This member can be one of the following values.
        /// <list type="bullet">
        /// <listheader>
        /// <term>State value</term>
        /// <description>Meaning</description>
        /// </listheader>
        /// <item><term>MEM_COMMIT 0x1000</term>
        /// <description>Indicates committed pages for which physical storage has been allocated, 
        /// either in memory or in the paging file on disk.</description></item>
        /// <item><term>MEM_FREE 0x10000</term>
        /// <description>Indicates free pages not accessible to the calling process and available to be allocated. 
        /// For free pages, the information in the AllocationBase, AllocationProtect, Protect, and Type members is undefined.</description></item>
        /// <item><term>MEM_RESERVE 0x2000</term>
        /// <description>Indicates reserved pages where a range of the process's virtual address space is reserved 
        /// without any physical storage being allocated. For reserved pages, the information in the Protect member is undefined.</description></item>
        /// </list>
        /// </summary>
        public ProcessInterop.MemoryState State;
        //public uint State;
        /// <summary>
        /// The access protection of the pages in the region. This member is one of the values listed for the AllocationProtect member.
        /// </summary>
        public ProcessInterop.MemoryProtection Protect;
        /// <summary>
        /// The type of pages in the region. The following types are defined.
        /// <list type="bullet">
        /// <listheader>
        /// <term>State value</term>
        /// <description>Meaning</description>
        /// </listheader>
        /// <item><term>MEM_IMAGE 0x1000000</term>
        /// <description>Indicates that the memory pages within the region are mapped into the view of an image section.</description></item>
        /// <item><term>MEM_MAPPED 0x40000</term>
        /// <description>Indicates that the memory pages within the region are mapped into the view of a section.</description></item>
        /// <item><term>MEM_PRIVATE 0x20000</term>
        /// <description>Indicates that the memory pages within the region are private (that is, not shared by other processes).</description></item>
        /// </list>
        /// </summary>
        public ProcessInterop.MemoryType Type;
    }


}
